<!--

    Copyright (C) 2015 The Gravitee team (http://gravitee.io)

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

            http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

-->
<div fxLayout="column">
  <div fxLayout="row">
    <div fxLayout="column" fxFlex="70">
      <form #clientOAuth2Form="ngForm" (keydown.enter)="false" (ngSubmit)="update()" fxLayout="column">
        <div class="gv-form-section">
          <div class="gv-form-section-title">
            <h5>Grant flows</h5>
            <mat-divider></mat-divider>
          </div>
          <div fxLayout="row wrap">
            <div *ngFor="let grantType of grantTypes" class="oauth2-grant-type" >
              <mat-checkbox name="grantTypes"
                            value="{{grantType.value}}"
                            [checked]="grantType.checked"
                            (change)="selectGrantType($event)" [disabled]="readonly || grantType.disabled">
                <span style="font-size: 14px;">{{grantType.name | titlecase}}</span>
              </mat-checkbox>
            </div>
            <div *ngFor="let extensionGrant of customGrantTypes" class="oauth2-grant-type" [matTooltip]="customGrantTypeIsDisabled(extensionGrant) ? 'Only one '+ extensionGrant.grantType + ' is allowed' : null">
              <mat-checkbox name="customGrantTypes"
                            value="{{extensionGrant.value}}"
                            [checked]="extensionGrant.checked"
                            (change)="selectCustomGrantType($event)" [disabled]="customGrantTypeIsDisabled(extensionGrant)">
                <span style="font-size: 14px;">{{extensionGrant.name | titlecase}}</span>
              </mat-checkbox>
            </div>
          </div>
        </div>

        <div class="gv-form-section">
          <div class="gv-form-section-title">
            <h5>Public / Confidential</h5>
            <small>Clients capability of maintaining the confidentiality of their credentials.</small>
            <small>Native application or a web browser-based application are incapable of secure their credentials and should set Token Endpoint Auth Method to "none".</small>
            <mat-divider></mat-divider>
          </div>
          <div>
            <mat-form-field appearance="outline" floatLabel="always">
              <mat-label>Type</mat-label>
              <mat-select placeholder="Change the token auth method" name="tokenAuthMethod" [(ngModel)]="applicationOauthSettings.tokenEndpointAuthMethod" (ngModelChange)="tokenEndpointAuthMethodChanged($event)" [disabled]="readonly">
                <mat-option><em>Based on incoming request</em></mat-option>
                <mat-option *ngFor="let tokenEndpointAuthMethod of tokenEndpointAuthMethods" [value]="tokenEndpointAuthMethod.value" [disabled]="tokenEndpointAuthMethod.disabled">{{ tokenEndpointAuthMethod.name }}</mat-option>
              </mat-select>
              <mat-hint>Requested Client Authentication method for the Token, Introspection and Revoke endpoints.</mat-hint>
            </mat-form-field>
          </div>
        </div>

        <div class="gv-form-section" *ngIf="applicationOauthSettings.tokenEndpointAuthMethod === 'tls_client_auth'">
          <div class="gv-form-section-title">
            <h5>Advanced MTLS Client authentication</h5>
            <small>Mutual TLS for OAuth Client Authentication requires a single subject distinguished name (DN) or a single subject alternative name (SAN) to authenticate the client. Only one subject name value of any type is used for each client.</small>
            <mat-divider></mat-divider>
          </div>
          <div fxLayout="column">
            <div fxLayout="column" fxLayoutGap="10px">
              <mat-form-field appearance="outline" floatLabel="always" fxFlex="32">
                <input matInput type="text" placeholder="subject distinguished name" name="tlsClientAuthSubjectDn" (ngModelChange)="modelChanged($event)" [(ngModel)]="applicationOauthSettings.tlsClientAuthSubjectDn" [disabled]="readonly" />
                <mat-hint>The expected subject distinguished name of the certificate (as defined in RFC4514)</mat-hint>
              </mat-form-field>
            </div>
            <div fxLayout="column" fxLayoutGap="10px">
              <mat-form-field appearance="outline" floatLabel="always" fxFlex="32">
                <input matInput type="text" placeholder="dNSName SAN entry" name="tlsClientAuthSanDns" (ngModelChange)="modelChanged($event)" [(ngModel)]="applicationOauthSettings.tlsClientAuthSanDns" [disabled]="readonly" />
                <mat-hint>The expected dNSName SAN entry in the certificate</mat-hint>
              </mat-form-field>
              <mat-form-field appearance="outline" floatLabel="always" fxFlex="32">
                <input matInput type="text" placeholder="uniformResourceIdentifier SAN entry" name="tlsClientAuthSanUri" (ngModelChange)="modelChanged($event)" [(ngModel)]="applicationOauthSettings.tlsClientAuthSanUri" [disabled]="readonly" />
                <mat-hint>The expected uniformResourceIdentifier SAN entry in the certificate</mat-hint>
              </mat-form-field>
              <mat-form-field appearance="outline" floatLabel="always" fxFlex="32">
                <input matInput type="text" placeholder="iPAddress SAN entry" name="tlsClientAuthSanIp" (ngModelChange)="modelChanged($event)" [(ngModel)]="applicationOauthSettings.tlsClientAuthSanIp" [disabled]="readonly" />
                <mat-hint>The expected iPAddress SAN entry in the certificate</mat-hint>
              </mat-form-field>
              <mat-form-field appearance="outline" floatLabel="always" fxFlex="32">
                <input matInput type="text" placeholder="rfc822Name SAN entry" name="tlsClientAuthSanEmail" (ngModelChange)="modelChanged($event)" [(ngModel)]="applicationOauthSettings.tlsClientAuthSanEmail" [disabled]="readonly" />
                <mat-hint>The expected rfc822Name SAN entry in the certificate</mat-hint>
              </mat-form-field>
            </div>
          </div>
        </div>

        <div class="gv-form-section">
          <div class="gv-form-section-title">
            <h5>Scopes</h5>
            <small>You can add default scopes to the tokens to control application's access to protected resources/APIs.</small>
            <mat-divider></mat-divider>
          </div>
          <div fxLayout="column">
            <div fxLayout="row" style="align-items: baseline;">
              <div fxLayout="column" style="margin-bottom: 20px;">
                <mat-slide-toggle
                  (change)="enhanceScopesWithUserPermissions($event)"
                  [checked]="isScopesEnhanceWithUserPermissions()" [disabled]="readonly">
                  Enhance scopes
                </mat-slide-toggle>
                <mat-hint style="font-size: 75%;">Enhance client scopes with user permissions</mat-hint>
              </div>
            </div>
            <div fxLayout="column">
              <application-scope *ngIf="!readonly" [scopes]="scopes" (addScopeChange)="addScope($event)"></application-scope>
              <ngx-datatable class="material"
                             [columnMode]="'flex'"
                             [headerHeight]="40"
                             [footerHeight]="40"
                             [rowHeight]="55"
                             [messages]="{emptyMessage:'There is no scopes'}"
                             [rows]='selectedScopes'>
                <ngx-datatable-column name="Scopes" [flexGrow]="2">
                  <ng-template let-row="row" let-rowIndex="rowIndex" ngx-datatable-cell-template>
                    <mat-icon style="vertical-align:middle;">donut_large</mat-icon>
                    <span>{{ row.key }}</span> | <small>{{row.name}}</small>
                  </ng-template>
                </ngx-datatable-column>
                <ngx-datatable-column name="" [flexGrow]="2">
                  <ng-template let-row="row" let-rowIndex="rowIndex" ngx-datatable-cell-template>
                    <div>
                      <span *ngIf="!(scopeApprovalExists(row.key))" style="color: grey; font-size: 85%; font-style: italic;">No time set</span>
                      <span *ngIf="(scopeApprovalExists(row.key))">{{ getScopeApproval(row.key) }}</span>
                    </div>
                    <div>
                      <small style="color: grey;">
                        ( default value :
                        <span *ngIf="!row.expiresIn" style="font-style: italic;">No time set</span>
                        <span *ngIf="row.expiresIn">{{ getScopeExpiry(row.expiresIn) }}</span>
                        )
                      </small>
                    </div>
                  </ng-template>
                </ngx-datatable-column>
                <ngx-datatable-column name="" [flexGrow]="1">
                  <ng-template let-row="row" ngx-datatable-cell-template>
                    <div fxLayout="row" class="application-scope-actions" *ngIf="!readonly">
                      <button mat-icon-button (click)="removeScope(row.key, $event)"><mat-icon>clear</mat-icon></button>
                    </div>
                  </ng-template>
                </ngx-datatable-column>
              </ngx-datatable>
            </div>
          </div>
        </div>

        <div class="gv-form-section">
          <div class="gv-form-section-title">
            <h5>Tokens</h5>
            <mat-divider></mat-divider>
          </div>

          <div fxLayout="column">
            <div fxLayout="row" fxLayoutGap="10px">
              <mat-form-field appearance="outline" floatLabel="always" fxFlex="32">
                <mat-icon matPrefix>timer</mat-icon>
                <input matInput type="number" placeholder="Access token validity" name="accessTokenValidity" min="0" (ngModelChange)="modelChanged($event)" [(ngModel)]="applicationOauthSettings.accessTokenValiditySeconds" [disabled]="readonly" />
                <mat-hint>Expiration of the Access tokens (seconds).</mat-hint>
              </mat-form-field>
              <mat-form-field appearance="outline" floatLabel="always" fxFlex="32">
                <mat-icon matPrefix>timer</mat-icon>
                <input matInput type="number" placeholder="Refresh token validity" name="refreshTokenValidity" min="0" (ngModelChange)="modelChanged($event)" [(ngModel)]="applicationOauthSettings.refreshTokenValiditySeconds" [disabled]="readonly" />
                <mat-hint>Expiration of the Refresh tokens (seconds).</mat-hint>
              </mat-form-field>
              <mat-form-field appearance="outline" floatLabel="always" fxFlex="32">
                <mat-icon matPrefix>timer</mat-icon>
                <input matInput type="number" placeholder="ID token validity" name="idTokenValidity" min="0" (ngModelChange)="modelChanged($event)" [(ngModel)]="applicationOauthSettings.idTokenValiditySeconds" [disabled]="readonly" />
                <mat-hint>Expiration of the ID tokens (seconds).</mat-hint>
              </mat-form-field>
            </div>
          </div>
        </div>

        <div class="gv-form-section">
          <div class="gv-form-section-title">
            <div fxLayout="row" style="align-items: center;">
              <h5 style="margin: 0;">Custom claims</h5>
              <button mat-icon-button (click)="openDialog($event)"><mat-icon>info_outline</mat-icon></button>
            </div>
            <small>You can add custom claims to the tokens (ID Token and Access Token) by picking attributes from the execution context.</small>
            <mat-divider></mat-divider>
          </div>
          <div class="token-custom-claims">
            <app-create-claim *ngIf="!readonly" (addClaimChange)="addClaim($event)"></app-create-claim>
            <p *ngIf="!claimsIsEmpty() && !readonly"><small><i>Double click to edit and press enter to save changes</i></small></p>
            <ngx-datatable class="material"
                           [columnMode]="'flex'"
                           [headerHeight]="40"
                           [footerHeight]="40"
                           [rowHeight]="55"
                           [messages]="{emptyMessage:'There is no custom claims'}"
                           [rows]='applicationOauthSettings.tokenCustomClaims'
                           [groupRowsBy]="'tokenType'"
                           [groupExpansionDefault]="true">
              <!-- Group Header Template -->
              <ngx-datatable-group-header [rowHeight]="50">
                <ng-template let-group="group" let-expanded="expanded" ngx-datatable-group-header-template>
                  <div style="padding:5px;">
                    <a
                      href="javascript:void(0);" style="color: grey; text-decoration: none; font-size: 14px;"
                      [class.datatable-icon-right]="!expanded"
                      [class.datatable-icon-down]="expanded"
                      title="Expand/Collapse Group"
                      (click)="toggleExpandGroup(group)">
                      {{ group.value[0].tokenType | uppercase }}
                    </a>
                  </div>
                </ng-template>
              </ngx-datatable-group-header>

              <ngx-datatable-column name="Claims" [flexGrow]="2">
                <ng-template let-row="row" let-rowIndex="rowIndex" ngx-datatable-cell-template>
                <span (dblclick)="editing[row.id + '-claimName'] = true" *ngIf="!editing[row.id + '-claimName']">
                  {{row.claimName}}
                </span>
                  <mat-form-field *ngIf="editing[row.id + '-claimName']" class="datatable-input">
                    <input matInput type="text" required autofocus placeholder="Claim name"
                           (keyup.enter)="updateClaim(row.tokenType, $event, 'claimName', row.id)"
                           (blur)="editing[row.id + '-claimName'] = false"
                           [value]="row.claimName" />
                  </mat-form-field>
                </ng-template>
              </ngx-datatable-column>
              <ngx-datatable-column name="" [flexGrow]="4">
                <ng-template let-row="row" let-rowIndex="rowIndex" ngx-datatable-cell-template>
                <pre style="margin: 0px; white-space: normal; font-size: 12px;" (dblclick)="editing[row.id + '-claimValue'] = true" *ngIf="!editing[row.id + '-claimValue']">
                  {{row.claimValue}}
                </pre>
                  <mat-form-field *ngIf="editing[row.id + '-claimValue']" class="datatable-input">
                    <input matInput type="text" required autofocus placeholder="Claim value"
                           (keyup.enter)="updateClaim(row.tokenType, $event, 'claimValue', row.id)"
                           (blur)="editing[row.id + '-claimValue'] = false"
                           [value]="row.claimValue" />
                  </mat-form-field>
                </ng-template>
              </ngx-datatable-column>
              <ngx-datatable-column name="" [flexGrow]="1">
                <ng-template let-row="row" ngx-datatable-cell-template>
                  <div fxLayout="row" class="provider-claims-actions" *ngIf="!readonly">
                    <button mat-icon-button (click)="deleteClaim(row.tokenType, row.claimName, $event)"><mat-icon>close</mat-icon></button>
                  </div>
                </ng-template>
              </ngx-datatable-column>
            </ngx-datatable>
          </div>
        </div>

        <div fxLayout="row" style="margin-bottom: 20px;">
          <button mat-raised-button [disabled]="!formChanged" type="submit">SAVE</button>
        </div>
      </form>
    </div>
    <div class="gv-page-description" fxFlex>
      <h3>OAuth 2.0 / OpenID Connect</h3>
      <div class="gv-page-description-content">
        <h4>Grant types</h4>
        <p>
          To authenticate an user, the application must first decide which oauth2 flow must be used. Oauth2 flows are enabled on the client through the grant_type.
          You should select the grant_type according to your application type.
        </p>
        <small>
          <b><i>authorization_code</i></b> for server side applications (web) or native applications (then combined with PKCE).<br>
          <b><i>implicit</i></b> must be used for Single Page Applications.<br>
          <b><i>password</i></b> should only be used for trusted applications <i>(means application and authorization server are managed within the same organization.)</i><br>
          <b><i>client_credentials</i></b> should be used for server to server communications.<br><br>
          <b>It is not recommended to mix client and user authentication within the same application.</b> <i>(Aka client_credentials and authorization_code, implicit or password)</i><br>
        </small>

        <h4>ID Token</h4>
        <p>
          The ID Token is a JSON Web Token (JWT) that contains claims (user profile information) about the End-User.
        </p>
        <p>
          You must provide the OAuth 2 scope <span class="code">openid</span> in order to get the <span class="code">id_token</span> which contains the following default claims :
        </p>
        <small>
          <b><i>iss:</i></b> Issuer Identifier for the Issuer of the response.<br>
          <b><i>sub:</i></b> Subject Identifier.<br>
          <b><i>aud:</i></b> Audience(s) that this ID Token is intended for.<br>
          <b><i>exp:</i></b> Expiration time on or after which the ID Token MUST NOT be accepted for processing.<br>
          <b><i>iat:</i></b> Time at which the JWT was issued.<br>
        </small>
      </div>
    </div>
  </div>
</div>
